//数据来源：https://www.mca.gov.cn/mzsj/xzqh/2022/202201xzqh.html
//2022年中华人民共和国县以上行政区划代码
//处理成特定json后导入

use serde::Deserialize;
use std::collections::HashMap;
use std::str::FromStr;
pub fn check_id_card(id_card_no: &str) -> String {
    match IdCard::from_str(id_card_no) {
        Ok(id_card) => {
            let gender = if id_card.is_male() { "男" } else { "女" };
            let birthday = format!(
                "{}年{:02}月{:02}日",
                id_card.birth_year, id_card.birth_month, id_card.birth_day
            );

            let (direct_cities, provinces) = parse();

            if let Some(location) = id_card.get_location(&direct_cities, &provinces) {
                return format!("身份证号码正确,{},{},{}", gender, birthday, location);
            } else {
                return "身份证号码错误".to_string();
            }
        }
        Err(_) => "身份证号码错误".to_string(),
    }
}

#[derive(Debug)]
struct IdCard {
    id: String,
    birth_year: u32,
    birth_month: u32,
    birth_day: u32,
}

impl IdCard {
    fn is_male(&self) -> bool {
        // 判断性别, 根据15位和18位身份证倒数第二或第三位判断性别
        let gender_digit = if self.id.len() == 18 {
            self.id.chars().nth(16).unwrap()
        } else {
            self.id.chars().last().unwrap()
        };
        gender_digit.to_digit(10).unwrap() % 2 != 0
    }

    fn get_location(&self, direct_cities: &DirectCities, provinces: &Provinces) -> Option<String> {
        let pre_code = self.id[0..2].parse::<u8>().ok()?;
        let city_code = self.id[0..4].parse::<u16>().ok()?;
        let district_code = self.id[0..6].parse::<u32>().ok()?;

        if let Some(direct_city) = direct_cities.direct_cities.get(&pre_code) {
            if let Some(district) = direct_city.districts.get(&district_code) {
                return Some(format!(
                    "{}-市辖区-{}",
                    direct_city.direct_city_name, district,
                ));
            }
        } else if let Some(province) = provinces.provinces.get(&pre_code) {
            if let Some(city) = province.cities.get(&city_code) {
                if let Some(district) = city.districts.get(&district_code) {
                    return Some(format!(
                        "{}-{}-{}",
                        province.province_name, city.city_name, district
                    ));
                }
            }
        }

        None
    }
}

impl FromStr for IdCard {
    type Err = &'static str;

    fn from_str(id: &str) -> Result<Self, Self::Err> {
        if id.len() == 18 {
            if !is_valid_id_18(id) {
                return Err("error id");
            }
            let (year, month, day) = parse_birthday_id(&id[6..14])?;
            Ok(IdCard {
                id: id.to_string(),
                birth_year: year,
                birth_month: month,
                birth_day: day,
            })
        } else if id.len() == 15 {
            let (year, month, day) = parse_birthday_id(&id[6..12])?;
            // println!("year: {}, month: {}, day: {}", year, month, day);
            Ok(IdCard {
                id: id.to_string(),
                birth_year: year,
                birth_month: month,
                birth_day: day,
            })
        } else {
            Err("error id")
        }
    }
}

fn parse_birthday_id(date_str: &str) -> Result<(u32, u32, u32), &'static str> {
    if date_str.len() == 8 {
        let year = date_str[0..4].parse::<u32>().map_err(|_| "Invalid year")?;
        let month = date_str[4..6].parse::<u32>().map_err(|_| "Invalid month")?;
        let day = date_str[6..8].parse::<u32>().map_err(|_| "Invalid day")?;
        if month < 1 || month > 12 {
            return Err("month is invalid");
        }
        let days = match_month_days(year, month)?;
        if day < 1 || day > days {
            return Err("day is invalid");
        }
        Ok((year, month, day))
    } else if date_str.len() == 6 {
        let year = date_str[0..2].parse::<u32>().map_err(|_| "Invalid year")?;
        let month = date_str[2..4].parse::<u32>().map_err(|_| "Invalid month")?;
        let day = date_str[4..6].parse::<u32>().map_err(|_| "Invalid day")?;
        if month < 1 || month > 12 {
            return Err("month is invalid");
        }
        let days = match_month_days(year + 1900, month)?;
        if day < 1 || day > days {
            return Err("day is invalid");
        }
        Ok((year + 1900, month, day))
    } else {
        Err("date is invalid")
    }
}

fn parse() -> (DirectCities, Provinces) {
    let direct_cities = "direct_cities.json";
    let provinces = "provinces.json";
    let direct_cities = std::fs::read_to_string(direct_cities).unwrap();
    let provinces = std::fs::read_to_string(provinces).unwrap();
    let direct_cities: DirectCities = serde_json::from_str(&direct_cities).unwrap();
    let provinces: Provinces = serde_json::from_str(&provinces).unwrap();
    (direct_cities, provinces)
}

fn is_valid_id_18(id: &str) -> bool {
    let check_code = vec![1, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2];
    let multied_codes = vec![7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
    let sum: u32 = id
        .chars()
        .take(17)
        .enumerate()
        .map(|(i, c)| c.to_digit(10).unwrap() * multied_codes[i])
        .sum();
    let remainder = (sum % 11) as usize;
    let last_code = id.chars().last().unwrap();
    let check_digit = if last_code == 'X' {
        10
    } else {
        last_code.to_digit(10).unwrap()
    };
    check_code[remainder] == check_digit
}

fn match_month_days(year: u32, month: u32) -> Result<u32, &'static str> {
    match month {
        1 | 3 | 5 | 7 | 8 | 10 | 12 => Ok(31),
        4 | 6 | 9 | 11 => Ok(30),
        2 if is_leap_year(year) => Ok(29),
        2 => Ok(28),
        _ => Err("Invalid month"),
    }
}

fn is_leap_year(year: u32) -> bool {
    (year % 4 == 0 && year % 100 != 0) || year % 400 == 0
}

// 定义 JSON 结构
#[derive(Debug, Deserialize)]
struct DirectCity {
    direct_city_name: String,
    pre_code: u8,
    districts: HashMap<u32, String>,
}

#[derive(Debug, Deserialize)]
struct DirectCities {
    direct_cities: HashMap<u8, DirectCity>,
}

#[derive(Debug, Deserialize)]
struct Provinces {
    provinces: HashMap<u8, Province>,
}

#[derive(Debug, Deserialize)]
struct Province {
    province_name: String,
    pre_code: u8,
    cities: HashMap<u16, City>,
}

#[derive(Debug, Deserialize)]
struct City {
    city_name: String,
    pre_code: u16,
    districts: HashMap<u32, String>,
}
